package com.bangcommunity.bbframe.mlog.logger;

import com.bangcommunity.bbframe.mlog.MLog;
import com.bangcommunity.bbframe.common.utils.thread.CommonThreadPoolManager;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author tanghc
 * @date 18/1/29
 */
public abstract class AsynQueuedMLogTracer {
    private static volatile int length = 10240000;
    private static String comm_trace = "common";
    private final static ConcurrentMap<String, BlockingQueue<TraceBean>> recordsMap = new ConcurrentHashMap<>();

    static Logger logger = LoggerFactory.getLogger(AsynQueuedMLogTracer.class);

    public static boolean isTraceEnable() {
        return logger.isInfoEnabled();
    }

    public static void trace(String format, Object... args) {
        traceByKey(comm_trace, format, args);
    }

    public static void trace(String mkey, String format, Object... args) {
        TraceBean traceBean = new TraceBean(format, args);
        BlockingQueue<TraceBean> queue = getReferenceInitIfNull(mkey);
        try {
            //todo 会阻塞业务代码
            queue.put(traceBean);
        } catch (InterruptedException e) {
            logger.error("put tracebean error: {},{}", mkey, format, e);
        }
    }

    public static void traceByKey(String key, String format, Object... args) {
        String mkey = MLog.createMkey(key);
        trace(mkey, format, args);
    }

    private static BlockingQueue<TraceBean> getReferenceInitIfNull(String mkey) {
        BlockingQueue<TraceBean> reference = recordsMap.get(mkey);
        if (reference == null) {
            BlockingQueue<TraceBean> queue = recordsMap.putIfAbsent(mkey, new LinkedBlockingQueue<>(length));
            reference = recordsMap.get(mkey);
            if (null == queue) {
                // 并发情况下只有一个启动线程监听消费
                listenerTrace(mkey, reference);
            }
        }
        return reference;
    }

    private static void listenerTrace(String mkey, final BlockingQueue<TraceBean> mLogRecordBeans) {
        if (null != mLogRecordBeans) {
            Logger traceLogger = LogFacilityFacade.createLogger(MLoggerName.mlog_trace, mkey);
            CommonThreadPoolManager.getInstance().addThread(() -> {
                while (true) {
                    try {
                        TraceBean record = mLogRecordBeans.take();
                        traceLogger.info(record.getFormat(), record.getArgs());
                        // traceLogger.info("{}", "");
                    } catch (InterruptedException e) {
                        // ignore
                    }
                }
            });
        }
    }

    @Data
    public static class TraceBean {
        private String format;
        private Object[] args;

        public TraceBean(String format, Object[] args) {
            this.format = format;
            this.args = args;
        }
    }
}